#include "thread_queue.h"

ThreadQueue::ThreadQueue()
    : m_hQueueThread( 0 )
{
}

ThreadQueue::~ThreadQueue()
{
}

BOOL ThreadQueue::Pause()
{
    if( !IsRunning() )
    {
        return FALSE;
    }

    AutoLocker_( m_hlocker );
    ::SuspendThread( m_hQueueThread );

    return TRUE;
}

BOOL ThreadQueue::Continue()
{
    if( !IsRunning() )
    {
        return FALSE;
    }

    AutoLocker_( m_hlocker );
    ::ResumeThread( m_hQueueThread );

    return FALSE;
}

DWORD ThreadQueue::Run( DWORD dwTimeout )
{
    AutoLocker_( m_hlocker );
    if( (HANDLE)m_hEventMsg && !m_hQueueThread )
    {
        ThreadParam * pParam = new ThreadParam;
        if( pParam )
        {
            pParam->pThis = this;
            pParam->dwTimeout = dwTimeout;
            m_hQueueThread = (HANDLE) ::_beginthreadex( 0, 0, QueueThread, ( void * )pParam, 0, 0 );
        }
    }

    return 0;
}

DWORD ThreadQueue::Quit( DWORD dwTimeout, DWORD dwRetCode )
{
    m_hlocker.Lock();
    if( m_hQueueThread )
    {
        m_hlocker.Unlock();
        m_hEvent.Set();
        PostQuitMessage( dwRetCode );
        DWORD dwRet = WaitForSingleObject( m_hQueueThread, dwTimeout );

        AutoLocker_( m_hlocker );
        if( dwRet == WAIT_TIMEOUT )
        {
            AutoLocker_( m_qlocker );
            ::TerminateProcess( m_hQueueThread, 0 );
        }
        ::CloseHandle( m_hQueueThread );
        m_hQueueThread = 0;
    }
    else
    {
        m_hlocker.Unlock();
    }

    Clear();

    return 0;
}

BOOL ThreadQueue::PostQueueTask( ITaskDelegate * pTaskDelegate, DWORD nTask, ITaskHandler * pTaskHandler )
{
    if( !pTaskDelegate || !pTaskHandler )
    {
        return FALSE;
    }

    Task task( pTaskDelegate, nTask, pTaskHandler, m_hEvent );
    return PostQueueTask( task );
}

BOOL ThreadQueue::IsCurrentThread( DWORD dwThreadId )
{
    if( !IsRunning() )
    {
        return FALSE;
    }

    AutoLocker_( m_hlocker );
    DWORD dwQueueThreadId = ::GetThreadId( m_hQueueThread );
    return dwThreadId == dwQueueThreadId;
}

BOOL ThreadQueue::IsRunning()
{
    AutoLocker_( m_hlocker );
    return m_hQueueThread != 0;
}

DWORD ThreadQueue::QueueLoop( DWORD dwTimeout )
{
    DWORD dwRetCode = 0;
    while ( TRUE )
    {
        if( m_hEventMsg.Wait( dwTimeout ) )
        {
            BOOL bQuit = ProcessTask( dwRetCode );
            if( bQuit || m_hEvent.Wait(0) )
            {
                break;
            }
        }
        else
        {
            break;
        }
    }

    AutoLocker_( m_hlocker );
    if( m_hQueueThread )
    {
        ::CloseHandle( m_hQueueThread );
        m_hQueueThread = 0;
    }

    return dwRetCode;
}

unsigned int __stdcall ThreadQueue::QueueThread( void * lpParam )
{
    DWORD dwRetCode = 0;
    ThreadParam * pParam = (ThreadParam *)lpParam;
    if( pParam )
    {
        ThreadQueue * pThis = pParam->pThis;
        DWORD dwTimeout = pParam->dwTimeout;
        if( pThis )
        {
            dwRetCode = pThis->QueueLoop( dwTimeout );
        }
    }
    
    return dwRetCode;
}

void ThreadQueue::Clear()
{
    AutoLocker_( m_qlocker );
    for( list<Task>::iterator it = m_lstTask.begin(); it != m_lstTask.end(); it++ )
    {
        ITaskHandler * pTaskHandler = (*it).GetTaskHandler();
        if( pTaskHandler )
        {
            pTaskHandler->Drop();
        }
    }
    m_lstTask.clear();
}

BOOL ThreadQueue::ProcessTask( DWORD &dwRetCode )
{
    dwRetCode = 0;
    BOOL bQuit = FALSE;
    do 
    {
        Task task;
        m_qlocker.Lock();
        if( m_lstTask.empty() )
        {
            m_qlocker.Unlock();
            break;
        }
        else
        {
            task = m_lstTask.front();
            m_lstTask.pop_front();
            m_qlocker.Unlock();
        }

        if( task.GetQuitSymbol() )
        {
            dwRetCode = task.GetTask();
            bQuit = TRUE;
            break;
        }

        if( task.GetTaskDelegate() && task.GetTaskHandler() )
        {
            ITaskDelegate * pTaskDelegate = task.GetTaskDelegate();
            ITaskHandler * pTaskHandler = task.GetTaskHandler();
            HANDLE         hEvent = task.GetEventHandler();

            pTaskDelegate->OnDeglegate( task.GetTask(), pTaskHandler, hEvent );
        }

    } while (0);

    AutoLocker_( m_qlocker );
    if( m_lstTask.empty() )
    {
        m_hEventMsg.Reset();
    }

    return bQuit;
}

BOOL ThreadQueue::PostQueueTask( Task &task )
{
    if( !IsRunning() )
    {
        return FALSE;
    }

    AutoLocker_(m_qlocker);
    m_lstTask.push_back( task );
    m_hEventMsg.Set();
    return TRUE;
}

void ThreadQueue::PostQuitMessage( DWORD dwRetCode )
{
    Task task;
    task.SetQuitSymbol();
    task.SetTask( dwRetCode );

    PostQueueTask( task );
}